enum EventType { Mouse, Keyboard }

interface Event { timestamp: number; }
interface MouseEvent extends Event { x: number; y: number }
interface KeyEvent extends Event { keyCode: number }

function listenEvent(eventType: EventType, handler: (n: Event) => void) {
  /* ... */
}

// Unsound, but useful and common
// listenEvent(EventType.Mouse, (e: Event) => console.log(e.x + ',' + e.y));

// Undesirable alternatives in presence of soundness
listenEvent(EventType.Mouse, (e: Event) => console.log((<MouseEvent>e).x + ',' + (<MouseEvent>e).y));
listenEvent(EventType.Mouse, <(e: Event) => void>((e: MouseEvent) => console.log(e.x + ',' + e.y)));

// Still disallowed (clear error). Type safety enforced for wholly incompatible types
listenEvent(EventType.Mouse, (e: Event) => console.log(e));

function invokeLater(args: any[], callback: (...args: any[]) => void) {
  /* ... Invoke callback with 'args' ... */
}

// Unsound - invokeLater "might" provide any number of arguments
invokeLater([1, 2], (x, y) => console.log(x + ', ' + y));

// Confusing (x and y are actually required) and undiscoverable
invokeLater([1, 2], (x?, y?) => console.log(x + ', ' + y));

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) { }
}

class Size {
  feet: number;
  constructor(numFeet: number) { }
}

let a: Animal = new Animal('dog', 0);
let s: Size = new Size(1);

a = s;  // OK
s = a;  // OK

interface Empty<T> { }
let x: Empty<number>;
let y: Empty<string>;

x = y;  // OK, because y matches structure of x

interface NotEmpty<T> {
  data: T;
}
let num: NotEmpty<number>;
let str: NotEmpty<string>;

// num = str;  // Error,

type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
  name: string;
}

var people: LinkedList<Person>;
var p = people.name;
var p = people.next.name;
var p = people.next.next.name;
var p = people.next.next.next.name;

class BasicCalculator {
  public constructor(protected value: number = 0) { }
  public currentValue(): number {
    return this.value;
  }
  public add(operand: number): this {
    this.value += operand;
    return this;
  }
  public multiply(operand: number): this {
    this.value *= operand;
    return this;
  }
  // ... other operations go here ...
}

let v = new BasicCalculator(2)
  .multiply(5)
  .add(1)
  .currentValue()

class ScientificCalculator extends BasicCalculator {
  public constructor(value = 0) {
    super(value);
  }
  public sin() {
    this.value = Math.sin(this.value);
    return this;
  }
  // ... other operations go here ...
}

new ScientificCalculator(2)
  .multiply(5)
  .sin()
  .add(1)
  .currentValue();


namespace IndexTypes {
  function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
    return names.map(n => o[n]);
  }

  interface Person {
    name: string;
    age: number;
    addr?: string;
  }
  let person: Person = {
    name: 'Jarid',
    age: 35
  };
  let strings: string[] = pluck(person, ['name', 'addr']); // ok, string[]
  // let strings: string[] = pluck(person, ['name', 'age', 'a']); // error, 不能将类型“"a"”分配给类型“keyof Person”。

  function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]; // o[name] is of type T[K]
  }
  let name: string = getProperty(person, 'name');
  let age: number = getProperty(person, 'age');
  // let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
  interface Map<T> {
    [key: string]: T;
  }
  let keys: keyof Map<number>; // string
  let value: Map<number>['foo']; // number
}